package org.ainlolcat.ainscience.entity;

import org.ainlolcat.ainscience.Controller;
import org.ainlolcat.ainscience.column.Column;
import org.ainlolcat.ainscience.entity.links.Link;
import org.ainlolcat.ainscience.matrix.Matrix;
import org.ainlolcat.ainscience.matrix.MatrixFile;
import org.ainlolcat.ainscience.matrix.MatrixFileParser;
import org.ainlolcat.ainscience.project.Project;
import org.ainlolcat.ainscience.project.ProjectFileHandler;
import org.ainlolcat.ainscience.templates.Template;
import org.ainlolcat.ainscience.tools;
import org.ainlolcat.ctp.util.IDGenerator;
import org.apache.log4j.Logger;
import org.jdom.Element;

import java.io.IOException;
import java.util.Collection;
import java.util.List;

/**
 * @author ainlolcat
 * Date: 12/6/11
 */
public class SimpleEntityParser implements EntityParser{
    private static Logger log = Logger.getLogger(SimpleEntityParser.class);

    public Entity load(Element e) throws IOException {
        //loadTemplates common data
        String e_name = e.getAttributeValue("name"),
                e_file = e.getAttributeValue("file")
                        .replace("%HOME%",Controller.getInstance().getProject().getHomeDir())
                        .replace("%ROOT%", "./"),
                e_template_name = e.getAttributeValue("template"),
                e_entity_type = e.getAttributeValue("entityType"),
                e_matrix_num = e.getAttributeValue("matrixNum");

        //loadTemplates links if possible
        Collection<Link> links = tools.parseLinks(e.getChildren("link"), null).values();

        //getData Template from project
        Template template = Controller.getInstance().
                    getProject().getTemplateByName(e_template_name);

        //TODO add conflict resolver for null template later

        //loadTemplates and parse Matrix
        MatrixFile matrixs = new MatrixFileParser().parseFile(template.getMatrixParserParams(),e_file);
        Matrix matrix = matrixs.getMatrix(tools.parseInteger(e_matrix_num));
        //TODO add conflict resolver for null or empty matrix later

        //parse entity from matrix
        //getData basic information and only static column
        SimpleEntity entity = getStaticColumnFromMatrix(matrix, template, e_entity_type);

        //and now we will apply different changes to our columns
        try{
            //TODO check (SimpleEntity) for replace
            entity = (SimpleEntity)new EntityTransformer().applyChangesToEntity(entity);
        }catch(Exception ex){
            log.error(ex);
        }

        //calculate column now
        //1st add links if they exist
        for (Link link:links){
            entity.setLink(link);
            link.setEntity(entity);
        }
        //TODO check (SimpleEntity) for replace
        try{
            entity = (SimpleEntity)new EntityCalculator().calculate(entity);
        }catch(Exception ex){
            log.error(ex);
        }

        try{
            entity = (SimpleEntity)new EntityCustomColumnGenerator().generateCustomColumns(entity);
        }catch(Exception ex){
            log.error(ex);
        }

        //add some information about entity
        //not critical params. can be null
        entity.setID(IDGenerator.getNextIntegerID());
        //critical params. must be
        entity.setName(e_name);
        entity.setFilename(e_file);
        entity.setMatrixNum(tools.parseInteger(e_matrix_num));
        return entity;
    }

    public Entity load(Matrix matrix, Element e) {
        //loadTemplates common data
        String e_name = e.getAttributeValue("name");

        //parse entity from matrix
        //getData basic information and only static column
        SimpleEntity entity = getStaticColumnFromMatrixByDesc(matrix, e);

        //loadTemplates links if possible
        Collection<Link> links = tools.parseLinks(e.getChildren("link"), entity).values();

        //calculate column now
        //1st add links if they exist
        for (Link link:links){
            entity.setLink(link);
        }

        try {
            update(entity);
        } catch (UnsupportedEntityTypeException e1) {
            log.error(e1);
        }

        //add some information about entity
        //not critical params. can be null
        //critical params. must be
        entity.setName(e_name);
        entity.setPersistent(false);
        return entity;
    }

    public void update(Entity entity) throws UnsupportedEntityTypeException {
        if (!(entity instanceof SimpleEntity)) throw new UnsupportedEntityTypeException();
        if (Controller.getInstance().getProject()==null) return;
        List<String> dep = entity.getDependencies();
        for (String name:dep){
            if (Controller.getInstance().getProject().getEntityByName(name)==null){
                return;
            }
        }
        //and now we will apply different changes to our columns
        try{
            new EntityTransformer().applyChangesToEntity(entity);
        }catch(Exception ex){
            log.error(ex);
        }

        try{
            new EntityCalculator().calculate(entity);
        }catch(Exception ex){
            log.error(ex);
        }

        try{
            new EntityCustomColumnGenerator().generateCustomColumns(entity);
        }catch(Exception ex){
            log.error(ex);
        }
    }
    
    private SimpleEntity getStaticColumnFromMatrix(Matrix matrix, Template template, String type){
        //type define which set of rule from template will be implemented to matrix
        //template contain only one rule for matrix but can contaion multiple set of rule for different type of entity
        Element parseRules = template.getEntityParseParams(type);

        //lets parse the description of column. Start from static one
        List<Element> staticColumns = parseRules.getChild("columns").getChildren("column");
        SimpleEntity entity = new SimpleEntity(template,type);
        for (Element columnDescription:staticColumns){
            entity.setColumn(
                    getStaticColumn(
                        matrix,
                        columnDescription
                    )
                    , tools.parseInteger(columnDescription.getAttributeValue("id")));
        }
        return entity;
    }

    private SimpleEntity getStaticColumnFromMatrixByDesc(Matrix matrix, Element description){
        //type define which set of rule from template will be implemented to matrix
        //template contain only one rule for matrix but can contaion multiple set of rule for different type of entity
        Template template =  Controller.getInstance().getProject().getTemplateByName(description.getAttributeValue("templateName"));
        Element parseRules = template.getEntityParseParams(description.getAttributeValue("entityType"));

        //lets parse the description of column. Start from static one
        List<Element> staticColumns = parseRules.getChild("columns").getChildren("column");
        SimpleEntity entity = new SimpleEntity(description);
        for (Element columnDescription:staticColumns){
            entity.setColumn(
                    getStaticColumn(
                            matrix,
                            columnDescription
                    )
                    , tools.parseInteger(columnDescription.getAttributeValue("id")));
        }
        return entity;
    }

    private Column getStaticColumn(Matrix matrix, Element description){
        //TODO add check for empty value
        String columnNumberStr = description.getAttributeValue("realColumn");
        Integer columnNumber = tools.parseInteger(columnNumberStr);
        Column column = matrix.getColumn(columnNumber);

        column.setName(description.getAttributeValue("name"));
        column.setAxis(description.getAttributeValue("axis"));
        column.setType(tools.parseInteger(description.getAttributeValue("type")));
        return column;
    }

    public Element save(Entity ent) throws UnsupportedEntityTypeException {
        if (ent instanceof SimpleEntity){
            SimpleEntity current = (SimpleEntity)ent;
            Element entity = new Element(ProjectFileHandler.ENTITY_TAG);
            Project project = Controller.getInstance().getProject();
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_NAME,current.getName());
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_FILE,current.getFilename().replace(project.getHomeDir(),"%HOME%"));
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_ID, current.getID().toString());
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_TEMPLATE_NAME, current.getTemplate().getName().toString());
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_MATRIX_NUM, current.getMatrixNum().toString());
            entity.setAttribute(ProjectFileHandler.ENTITY_ATTR_ENTITY_TYPE, current.getType());
            if (current.getLinks()!=null&&current.getLinks().size()>0){
                Collection<Link> links = current.getLinks();
                for (Link link:links){
                    Element elink = new Element(ProjectFileHandler.LINK_TAG);
                    elink.setAttribute(ProjectFileHandler.LINK_ATTR_NAME,link.getName());
                    elink.setAttribute(ProjectFileHandler.LINK_ATTR_COLUMN,link.getColumnID().toString());
                    elink.setAttribute(ProjectFileHandler.LINK_ATTR_L2COLUMN,link.getL2column().toString());
                    elink.setAttribute(ProjectFileHandler.LINK_ATTR_L2ENTITY,link.getL2Entity());
                    elink.setAttribute(ProjectFileHandler.LINK_ATTR_TYPE,link.getType());
                    entity.addContent(elink);
                }
            }
            return entity;
        } else {
            throw new UnsupportedEntityTypeException();
        }
    }
}
